home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume8 / vn / part02 < prev    next >
Encoding:
Internet Message Format  |  1987-02-16  |  43.1 KB

  1. Subject:  v08i065:  The VN news reader, Part02/03
  2. Newsgroups: mod.sources
  3. Approved: mirror!rs
  4.  
  5. Submitted by: rtech!rtech!bobm (Bob Mcqueer)
  6. Mod.sources: Volume 8, Issue 65
  7. Archive-name: vn/Part02
  8.  
  9. [  I forgot to point out that I repacked the archives because of a patch
  10.    Bob sent me.  --r$  ]
  11.  
  12. #! /bin/sh
  13. # This is a shell archive.  Remove anything before this line,
  14. # then unpack it by saving it in a file and typing "sh file".
  15. # If all goes well, you will see the message "End of archive 2 (of 3)."
  16. # Contents:  reader.c strtok.c term_set.c tty.h tty_set.c tune.h
  17. #   userlist.c vn.h vnglob.c
  18. # Wrapped by rs@mirror on Tue Feb 17 12:10:31 1987
  19. PATH=/bin:/usr/bin:/usr/ucb; export PATH
  20. echo shar: extracting "'reader.c'" '(18610 characters)'
  21. if test -f 'reader.c' ; then 
  22.   echo shar: will not over-write existing file "'reader.c'"
  23. else
  24. sed 's/^X//' >reader.c <<'@//E*O*F reader.c//'
  25. X/*
  26. X** vn news reader.
  27. X**
  28. X** reader.c - article reading interface - "more" like.
  29. X**
  30. X** see copyright disclaimer / history in vn.c source file
  31. X*/
  32. X
  33. X#include <stdio.h>
  34. X#include <sys/types.h>
  35. X#include "tty.h"
  36. X#include "config.h"
  37. X#include "vn.h"
  38. X#include "head.h"
  39. X#include "reader.h"
  40. X
  41. X#define PERTAB 8    /* tab expansion factor */
  42. X#define BACKTRACK 24
  43. X
  44. Xextern char *Printer,*Editor,*Mailer,*Poster,*Orgdir,*Savefile,*Savedir,*Ccfile;
  45. Xextern int L_allow;
  46. Xextern int C_allow;
  47. Xextern int Rot;
  48. Xextern int Headflag;
  49. Xextern int Digest;
  50. Xextern char *No_msg;
  51. Xextern char *Roton_msg;
  52. Xextern char *Rotoff_msg;
  53. Xextern char *Hdon_msg;
  54. Xextern char *Hdoff_msg;
  55. X
  56. Xextern char *T_head, *FT_head, *N_head, *L_head, *RT_head, *DIS_head;
  57. Xextern char *TO_head, *F_head, *P_head, *M_head, *R_head;
  58. X
  59. Xextern char Cxrtoi[], Cxitor[];
  60. X
  61. Xstatic FILE *Fpread;
  62. Xstatic char *Fname;
  63. Xstatic char *Lookahead;
  64. Xstatic int Rlines;
  65. Xstatic int Hlines;
  66. X
  67. X#ifdef ADDRMUNGE
  68. Xstatic int Newaddr;
  69. X#endif
  70. X
  71. X/*
  72. X    readstr routine is the "funnel" to the reading state,
  73. X    and controls signal setting.  Some "session" context is passed
  74. X    along to allow jumping back to a different display
  75. X
  76. X    WARNING:
  77. X
  78. X    NOTHING below readstr should call strtok()
  79. X*/
  80. X
  81. Xreadstr (s,crec,highrec,count)
  82. Xchar *s;
  83. Xint *crec, *highrec;
  84. Xint count;
  85. X{
  86. X    char *fnext, *strtok();
  87. X    int pc;
  88. X    Fname = strtok(s,LIST_SEP);
  89. X    if (Fname != NULL)
  90. X    {
  91. X        term_set (ERASE);
  92. X        sig_set (BRK_READ,&Fpread);
  93. X        fnext = strtok(NULL,LIST_SEP);
  94. X        while (Fname != NULL && readfile(fnext,&pc) >= 0)
  95. X        {
  96. X            if (Digest)
  97. X                unlink (Fname);
  98. X            Fname = fnext;
  99. X            fnext = strtok (NULL,LIST_SEP);
  100. X        }
  101. X        if (Digest && Fname != NULL)
  102. X            unlink (Fname);
  103. X        if (pc != 0)
  104. X            forward (pc, crec, highrec);
  105. X        else
  106. X        {
  107. X            *crec += count;
  108. X            if (*crec >= *highrec)
  109. X                *crec = *highrec - 1;
  110. X        }
  111. X        sig_set (BRK_RFIN);
  112. X        show ();
  113. X        term_set (MOVE, 0, *crec);
  114. X    }
  115. X    else
  116. X    {
  117. X        preinfo ("%s",No_msg);
  118. X        term_set (MOVE, 0, *crec);
  119. X    }
  120. X}
  121. X
  122. X/*
  123. X    readfile presents article:
  124. X        sn - name of next article, NULL if last.
  125. X        pages - pages to advance on return, if applicable
  126. X    returns 0 for "continue", <0 for "quit"
  127. X*/
  128. Xstatic readfile (sn,pages)
  129. Xchar *sn;
  130. Xint *pages;
  131. X{
  132. X    FILE *fopen();
  133. X    int lines,percent,artlin;
  134. X    long rew_pos, ftell();
  135. X    char c,  buf[RECLEN], mid[RECLEN], ngrp[RECLEN], dist[RECLEN];
  136. X     char from[RECLEN], title[RECLEN], flto[RECLEN], reply[RECLEN];
  137. X     char pstr[24], dgname[48], getpgch(), *index(), *digest_extract();
  138. X    char *tgetstr();
  139. X
  140. X    *pages = 0;
  141. X
  142. X    term_set(ERASE);
  143. X
  144. X    if (Digest)
  145. X    {
  146. X        lines = atoi(Fname);
  147. X        if ((Fname = digest_extract(dgname,lines)) == NULL)
  148. X        {
  149. X            printf ("couldn't extract article %d",lines);
  150. X            return (0);
  151. X        }
  152. X    }
  153. X
  154. X    if ((Fpread = fopen(Fname,"r")) == NULL)
  155. X    {
  156. X        printf ("couldn't open article %s",Fname);
  157. X        return (0);
  158. X    }
  159. X
  160. X    Hlines = gethead (mid, from, title, ngrp, flto, reply, dist, &artlin);
  161. X    printf (ANFORM,Fname,Cxrtoi[PG_HELP]);
  162. X    lines = 1;
  163. X    rew_pos = ftell(Fpread);
  164. X    if (Headflag)
  165. X    {
  166. X        rewind(Fpread);
  167. X        Rlines = 0;
  168. X    }
  169. X    else
  170. X    {
  171. X        /* use do_out to guard against control sequences */
  172. X        Rlines = Hlines;
  173. X        sprintf (buf,"%s%s\n",T_head,title);
  174. X        lines += do_out(buf,1);
  175. X        sprintf (buf,"%s%s\n",F_head,from);
  176. X        lines += do_out(buf,1);
  177. X        if (index(ngrp,',') != NULL)
  178. X        {
  179. X            sprintf (buf,"%s%s\n",N_head,ngrp);
  180. X            lines += do_out(buf,1);
  181. X        }
  182. X        if (*flto != '\0')
  183. X        {
  184. X            sprintf (buf,"%s%s\n",FT_head,flto);
  185. X            lines += do_out(buf,1);
  186. X        }
  187. X        printf ("%s%d\n",L_head,artlin);    /* no controls */
  188. X        ++lines;
  189. X    }
  190. X
  191. X    /* will return out of outer while loop */
  192. X    Lookahead = NULL;
  193. X    while (1)
  194. X    {
  195. X        /*
  196. X        ** lines counts folded lines from do_out.
  197. X        ** globals Hlines and Rlines refer to records.
  198. X        ** If Lookahead is null after this loop, we've
  199. X        ** hit EOF.
  200. X        */
  201. X        lines += do_out(Lookahead,L_allow-lines);
  202. X        while (1)
  203. X        {
  204. X            if (Lookahead == NULL)
  205. X            {
  206. X                if (fgets(buf,RECLEN-1,Fpread) == NULL)
  207. X                    break;
  208. X                Lookahead = buf;
  209. X                if (Rot != 0 && Rlines >= Hlines)
  210. X                    rot_line(buf);
  211. X                ++Rlines;
  212. X            }
  213. X            if (lines >= L_allow)
  214. X                break;
  215. X            lines += do_out(buf,L_allow-lines);
  216. X        }
  217. X
  218. X        if (Lookahead != NULL)
  219. X        {
  220. X            /*
  221. X            ** calculation is truncated rather than rounded,
  222. X            ** so we shouldn't get "100%".  Subtract 2 for
  223. X            ** 1 line lookahead and empty line at beginning
  224. X            ** of article.
  225. X            */
  226. X            if (Headflag)
  227. X                percent = ((Rlines-2)*100)/(artlin+Hlines);
  228. X            else
  229. X                percent = ((Rlines-Hlines-2)*100)/artlin;
  230. X            sprintf (pstr,PAGE_MID,percent);
  231. X        }
  232. X        else
  233. X        {
  234. X            if (sn == NULL)
  235. X                strcpy (pstr,PAGE_END);
  236. X            else
  237. X            strcpy (pstr,PAGE_NEXT);
  238. X        }
  239. X        c = getpgch(pstr,mid,from,reply,title,ngrp,flto,dist);
  240. X
  241. X        /*
  242. X            handle user input:
  243. X            CAUTION!!  return cases must close Fpread.
  244. X        */
  245. X        switch (c)
  246. X        {
  247. X        case PG_NEXT:
  248. X            fclose (Fpread);
  249. X            return (0);
  250. X        case PG_FLIP:
  251. X            *pages = 1;    /* fall through */
  252. X        case PG_QUIT:
  253. X            fclose (Fpread);
  254. X            return (-1);
  255. X        case PG_REWIND:
  256. X            if (Headflag)
  257. X            {
  258. X                Rlines = 0;
  259. X                rewind (Fpread);
  260. X            }
  261. X            else
  262. X            {
  263. X                fseek (Fpread,rew_pos,0);
  264. X                Rlines = Hlines;
  265. X            }
  266. X            Lookahead = NULL;
  267. X            lines = 2 - RECBIAS;
  268. X            break;
  269. X        case PG_SEARCH:
  270. X            searcher(buf);
  271. X            lines = 2 - RECBIAS;
  272. X            lines += do_out(buf,L_allow-lines);
  273. X            break;
  274. X        case PG_WIND:
  275. X            fseek (Fpread,0L,2);
  276. X            lines = 2 - RECBIAS;
  277. X            Lookahead = NULL;
  278. X            break;
  279. X        case PG_STEP:
  280. X            if (Lookahead == NULL)
  281. X            {
  282. X                fclose (Fpread);
  283. X                return (0);
  284. X            }
  285. X            lines = L_allow - 1;
  286. X            break;
  287. X        default:
  288. X            if (Lookahead == NULL)
  289. X            {
  290. X                fclose (Fpread);
  291. X                return (0);
  292. X            }
  293. X            lines = 2 - RECBIAS;
  294. X            break;
  295. X        }
  296. X    }
  297. X}
  298. X
  299. X/*
  300. X    gethead obtains subject, reply, message id, from, lines, newsgroup and
  301. X    followup-to lines of article for later use in mailing replies and
  302. X    posting followups, does not rewind, but leaves file at end of header
  303. X    lines.  Returns number of header lines.
  304. X*/
  305. Xstatic gethead (mid, from, title, ngrp, flto, reply, dist, lin)
  306. Xchar *mid, *from, *title, *ngrp, *flto, *reply, *dist;
  307. Xint *lin;
  308. X{
  309. X    int count;
  310. X    char buf [RECLEN], *index();
  311. X    long pos,ftell();
  312. X
  313. X#ifdef ADDRMUNGE
  314. X    Newaddr = 1;
  315. X#endif
  316. X
  317. X    *lin = 0;
  318. X    *dist = *mid = *from = *title = *ngrp = *flto = *reply = '\0';
  319. X
  320. X    /* for conditional is abnormal - expected exit is break */
  321. X    for (count = 0; count < HDR_LINES && fgets(buf,RECLEN-1,Fpread) != NULL; ++count)
  322. X    {
  323. X
  324. X        /* reset position and bail out at first non-header line */
  325. X        if (index(buf,':') == NULL)
  326. X        {
  327. X            pos = ftell(Fpread);
  328. X            pos -= strlen(buf);
  329. X            fseek (Fpread,pos,0);
  330. X            break;
  331. X        }
  332. X
  333. X#ifdef MAILSMART
  334. X        if (strncmp(buf,RT_head,RTHDLEN) == 0)
  335. X        {
  336. X            buf [strlen(buf)-1] = '\0';
  337. X            strcpy (reply,buf+RTHDLEN);
  338. X            continue;
  339. X        }
  340. X#else
  341. X        if (strncmp(buf,P_head,PHDLEN) == 0)
  342. X        {
  343. X            buf [strlen(buf)-1] = '\0';
  344. X            strcpy (reply,buf+PHDLEN);
  345. X            continue;
  346. X        }
  347. X#endif
  348. X        if (strncmp(buf,DIS_head,DISHDLEN) == 0)
  349. X        {
  350. X            buf [strlen(buf)-1] = '\0';
  351. X            strcpy (dist,buf+DISHDLEN);
  352. X            continue;
  353. X        }
  354. X        if (strncmp(buf,M_head,MHDLEN) == 0)
  355. X        {
  356. X            buf [strlen(buf)-1] = '\0';
  357. X            strcpy (mid,buf+MHDLEN);
  358. X            continue;
  359. X        }
  360. X        if (strncmp(buf,F_head,FHDLEN) == 0)
  361. X        {
  362. X            buf [strlen(buf)-1] = '\0';
  363. X            strcpy (from,buf+FHDLEN);
  364. X            continue;
  365. X        }
  366. X        if (strncmp(buf,T_head,THDLEN) == 0)
  367. X        {
  368. X            buf [strlen(buf)-1] = '\0';
  369. X            strcpy (title,buf+THDLEN);
  370. X            continue;
  371. X        }
  372. X        if (strncmp(buf,N_head,NHDLEN) == 0)
  373. X        {
  374. X            buf [strlen(buf)-1] = '\0';
  375. X            strcpy (ngrp,buf+NHDLEN);
  376. X            continue;
  377. X        }
  378. X        if (strncmp(buf,FT_head,FTHDLEN) == 0)
  379. X        {
  380. X            buf [strlen(buf)-1] = '\0';
  381. X            strcpy (flto,buf+FTHDLEN);
  382. X            continue;
  383. X        }
  384. X        if (strncmp(buf,L_head,LHDLEN) == 0)
  385. X        {
  386. X            buf [strlen(buf)-1] = '\0';
  387. X            *lin = atoi(buf+LHDLEN);
  388. X            continue;
  389. X        }
  390. X    }
  391. X#ifdef MAILSMART
  392. X    if (*reply == '\0')
  393. X        strcpy(reply,from);
  394. X#endif
  395. X    return (count);
  396. X}
  397. X
  398. X/*
  399. X    getpgch prints prompt and gets command from user
  400. X    handles "mail", "save" and "followup" internally
  401. X    as well as flag resets.
  402. X*/
  403. Xstatic char getpgch(prompt,mid,from,reply,title,ngrp,flto,dist)
  404. Xchar *prompt, *mid, *from, *reply, *title, *ngrp, *flto, *dist;
  405. X{
  406. X    char c;
  407. X    int ic;
  408. X    term_set (ONREVERSE);
  409. X    printf("%s\015",prompt);
  410. X    term_set (OFFREVERSE);
  411. X    while ((ic=getnoctl()) != EOF)
  412. X    {
  413. X        switch (c = Cxitor[ic])
  414. X        {
  415. X        case SETROT:
  416. X            term_set (ZAP,0,PPR_MAX);
  417. X            if (Rot == 0)
  418. X            {
  419. X                Rot = 13;
  420. X                printf ("%s\n",Roton_msg);
  421. X            }
  422. X            else
  423. X            {
  424. X                Rot = 0;
  425. X                printf ("%s\n",Rotoff_msg);
  426. X            }
  427. X            if (Lookahead != NULL && Rlines > Hlines)
  428. X                rot_line(Lookahead);
  429. X            break;
  430. X        case HEADTOG:
  431. X            term_set (ZAP,0,PPR_MAX);
  432. X            if (Headflag)
  433. X            {
  434. X                Headflag = FALSE;
  435. X                printf ("%s\n",Hdoff_msg);
  436. X            }
  437. X            else
  438. X            {
  439. X                Headflag = TRUE;
  440. X                printf ("%s\n",Hdon_msg);
  441. X            }
  442. X            break;
  443. X        case PG_HELP:
  444. X            term_set (ZAP,0,PPR_MAX);
  445. X            help_pg ();
  446. X            break;
  447. X        case PG_REPLY:
  448. X            mail (reply,title,from);
  449. X            break;
  450. X        case PG_FOLLOW:
  451. X            followup (mid,title,ngrp,flto,from,dist);
  452. X            break;
  453. X        case SAVE:
  454. X            saver ();
  455. X            break;
  456. X        case PRINT:
  457. X            printr ();
  458. X            break;
  459. X        default:
  460. X            term_set (ZAP,0,PPR_MAX);
  461. X            return (c);
  462. X        }
  463. X
  464. X        term_set (ONREVERSE);
  465. X        printf("%s\015",prompt);
  466. X        term_set (OFFREVERSE);
  467. X    }
  468. X    term_set (ZAP,0,PPR_MAX);
  469. X    return (c);
  470. X}
  471. X
  472. X/*
  473. X    save article
  474. X    Like the savestr routine, it "loses" some storage every time
  475. X    the user specifies a new file, but this should not be significant
  476. X*/
  477. Xstatic saver ()
  478. X{
  479. X    char *fn,cmd[RECLEN],*str_store(),*rprompt();
  480. X
  481. X    tty_set (SAVEMODE);
  482. X    sprintf (cmd,SAVFORM,Savefile);
  483. X    fn = rprompt(cmd,cmd);
  484. X    if (fn != NULL)
  485. X        Savefile = str_store(fn);
  486. X    if (*Savefile != '/' && *Savefile != '$')
  487. X        sprintf (cmd,"cat %s >>%s/%s",Fname,Savedir,Savefile);
  488. X    else
  489. X        sprintf (cmd,"cat %s >>%s",Fname,Savefile);
  490. X    system (cmd);
  491. X    tty_set (RESTORE);
  492. X}
  493. X
  494. X/*
  495. X    invoke editor on new temp file, mail using reply line,
  496. X    possibly first allowing user to overide the reply (not INLETTER)
  497. X*/
  498. Xstatic mail (p, t, f)
  499. Xchar *p, *t, *f;
  500. X{
  501. X    char *new, fn[L_tmpnam], cmd [RECLEN+60], *rprompt ();
  502. X    FILE *fp, *fopen();
  503. X
  504. X    tmpnam (fn);
  505. X    if ((fp = fopen(fn,"w")) == NULL)
  506. X        printex ("can't open %s\n",fn);
  507. X
  508. X    if ((new = index(p, '(')) != NULL)
  509. X        *new = '\0';    /* a poor way of deleting comments */
  510. X
  511. X#ifdef ADDRMUNGE
  512. X    if (Newaddr)
  513. X    {
  514. X        Newaddr = 0;
  515. X        ADDRMUNGE(p);
  516. X    }
  517. X#endif
  518. X
  519. X    if (strncmp(t, FPFIX, FPFLEN) == 0)
  520. X        t += FPFLEN;    /* don't add multiple Re:s */
  521. X#ifdef INLETTER
  522. X     fprintf (fp,"%s%s\n%s%s%s\n\n%s:\n", TO_head, p, T_head, FPFIX, t, f);
  523. X#else
  524. X    fprintf (fp,"%s%s%s\n\n%s:\n", T_head, FPFIX, t, f);
  525. X#endif
  526. X
  527. X    edcopy (fp);
  528. X    fclose (fp);
  529. X    tty_set (SAVEMODE);
  530. X
  531. X#ifndef INLETTER
  532. X    sprintf (cmd,"ADDRESS: %s\nreturn to accept, or input new address: ",p);
  533. X    if ((new = rprompt(cmd,cmd)) != NULL)
  534. X        strcpy (p,new);
  535. X#endif
  536. X
  537. X    sprintf (cmd,"%s %s", Editor, fn);
  538. X    chdir (Orgdir);
  539. X    system (cmd);
  540. X    cd_group ();
  541. X    new = rprompt ("still want to mail it ? ",cmd);
  542. X    if (new != NULL && (*new == 'y' || *new == 'Y'))
  543. X    {
  544. X#ifndef INLETTER
  545. X        sprintf (cmd,"%s '%s' <%s", Mailer, p, fn);
  546. X#else
  547. X        sprintf (cmd,"%s <%s", Mailer, fn);
  548. X#endif
  549. X        system (cmd);
  550. X        printf ("given to mailer\n");
  551. X    }
  552. X    else
  553. X        printf ("not mailed\n");
  554. X    unlink (fn);
  555. X    tty_set (RESTORE);
  556. X    term_set (RESTART);
  557. X}
  558. X
  559. X/*
  560. X    post a followup article, invoking editor for user after creating
  561. X    new temp file.  remove after posting.  Hack in ".followup" if posting
  562. X    newsgroup ends in ".general" - similar hack for preventing mod &
  563. X    announce groups - should really be more thorough and parse the
  564. X    whole string.  User can change, anyway.
  565. X*/
  566. Xstatic followup (m,t,n,ft,oa,dist)
  567. Xchar *m, *t, *n, *ft, *oa, *dist;
  568. X{
  569. X    char fn[L_tmpnam], *new, cmd [RECLEN], *rprompt();
  570. X    FILE *f, *fopen();
  571. X    char *rindex();
  572. X
  573. X    if (*ft != '\0')
  574. X        strcpy (cmd,ft);
  575. X    else
  576. X        strcpy (cmd,n);
  577. X    new = rindex(cmd,'.');
  578. X    if (new != NULL && strcmp(new,".general") == 0)
  579. X        strcpy (new,".followup");
  580. X    if ( strncmp(cmd, "mod.", 4) == 0 || strcmp(new, ".announce") == 0)
  581. X    {
  582. X        term_set (ONREVERSE);
  583. X        printf("Cannot post a follow-up to \"%s\", reply with mail to moderator\007\n",
  584. X            cmd);
  585. X        term_set (OFFREVERSE);
  586. X        return;
  587. X    }
  588. X
  589. X    tmpnam (fn);
  590. X    if ((f = fopen(fn,"w")) == NULL)
  591. X        printex ("can't open %s\n",fn);
  592. X
  593. X    if (strncmp(t, FPFIX, FPFLEN) == 0)
  594. X        t += FPFLEN;    /* don't add multiple Re:s */
  595. X    fprintf (f,"%s%s%s\n%s%s\n%s%s\n",T_head,FPFIX,t,N_head,cmd,R_head,m);
  596. X    if (*dist != '\0')
  597. X        fprintf(f,"%s%s\n",DIS_head,dist);
  598. X    fprintf (f,"\nin article %s, %s says:\n",m,oa);
  599. X    edcopy (f);
  600. X    fclose (f);
  601. X    tty_set (SAVEMODE);
  602. X    sprintf (cmd,"%s %s", Editor, fn);
  603. X    chdir (Orgdir);
  604. X    system (cmd);
  605. X    cd_group ();
  606. X    new = rprompt("still want to post it ? ",cmd);
  607. X    if (new != NULL && (*new == 'y' || *new == 'Y'))
  608. X    {
  609. X        sprintf (cmd,"%s <%s", Poster, fn);
  610. X        system (cmd);
  611. X        printf ("given to posting program\n");
  612. X        save_article (fn);
  613. X    }
  614. X    else
  615. X        printf ("not posted\n");
  616. X    unlink (fn);
  617. X    tty_set (RESTORE);
  618. X    term_set (RESTART);
  619. X}
  620. X
  621. X/*
  622. X    get user buffer, return whitespace delimited token
  623. X    without using strtok().  buffer is allowed to overwrite
  624. X    prompt string.
  625. X*/
  626. Xstatic char *
  627. Xrprompt(s,buf)
  628. Xchar *s,*buf;
  629. X{
  630. X    printf("%s",s);
  631. X    fgets (buf,RECLEN-1,stdin);
  632. X    while (*buf == ' ' || *buf == '\t')
  633. X        ++buf;
  634. X    if (*buf == '\n' || *buf == '\0')
  635. X        return (NULL);
  636. X    for (s = buf; *s != ' ' && *s != '\t' && *s != '\n' && *s != '\0'; ++s)
  637. X        ;
  638. X    *s = '\0';
  639. X    return (buf);
  640. X}
  641. X
  642. X/*
  643. X    edcopy copies article to file which user is editting for
  644. X    a reply or followup, so it may be referenced.  It places
  645. X    ED_MARK in the left hand margin.
  646. X*/
  647. Xedcopy(fp)
  648. XFILE *fp;
  649. X{
  650. X    long current;
  651. X    char buf[RECLEN];
  652. X    int i;
  653. X
  654. X    /* save position, rewind and skip over header lines */
  655. X    current = ftell(Fpread);
  656. X    rewind (Fpread);
  657. X    for (i=0; i < HDR_LINES; ++i)
  658. X    {
  659. X        if (fgets(buf,RECLEN-1,Fpread) == NULL)
  660. X            break;
  661. X        if (strncmp(buf,L_head,LHDLEN) == 0)
  662. X            break;
  663. X    }
  664. X
  665. X    /* if line already begins with ED_MARK, forget about the space */
  666. X    while (fgets(buf,RECLEN-1,Fpread) != NULL)
  667. X    {
  668. X        if (buf[0] == ED_MARK)
  669. X            fprintf(fp,"%c%s",ED_MARK,buf);
  670. X        else
  671. X            fprintf(fp,"%c %s",ED_MARK,buf);
  672. X    }
  673. X
  674. X    /* restore position */
  675. X    fseek(Fpread,current,0);
  676. X}
  677. X
  678. X/*
  679. X    help menus
  680. X*/
  681. Xstatic help_pg()
  682. X{
  683. X    h_print (Cxrtoi[PG_NEXT],HPG_NEXT);
  684. X    h_print (Cxrtoi[PG_QUIT],HPG_QUIT);
  685. X    h_print (Cxrtoi[PG_FLIP],HPG_FLIP);
  686. X    h_print (Cxrtoi[PG_REWIND],HPG_REWIND);
  687. X    h_print (Cxrtoi[PG_WIND],HPG_WIND);
  688. X    h_print (Cxrtoi[PG_SEARCH],HPG_SEARCH);
  689. X    h_print (Cxrtoi[PG_STEP],HPG_STEP);
  690. X    h_print (Cxrtoi[PG_REPLY],HPG_REPLY);
  691. X    h_print (Cxrtoi[PG_FOLLOW],HPG_FOLLOW);
  692. X    h_print (Cxrtoi[SAVE],HPG_SAVE);
  693. X    h_print (Cxrtoi[PRINT],HPG_PRINT);
  694. X    h_print (Cxrtoi[SETROT],HPG_ROT);
  695. X    h_print (Cxrtoi[HEADTOG],HPG_HEAD);
  696. X    h_print (Cxrtoi[PG_HELP],HPG_HELP);
  697. X    printf ("%s\n",HPG_DEF);
  698. X}
  699. X
  700. Xrot_line (s)
  701. Xunsigned char *s;
  702. X{
  703. X    for ( ; *s != '\0'; ++s)
  704. X    {
  705. X        if (*s >= 'A' && *s <= 'Z')
  706. X        {
  707. X            *s += Rot;
  708. X            if (*s > 'Z')
  709. X                *s -= 26;
  710. X            continue;
  711. X        }
  712. X        if (*s >= 'a' && *s <= 'z')
  713. X        {
  714. X            *s += Rot;
  715. X            if (*s > 'z')
  716. X                *s -= 26;
  717. X        }
  718. X    }
  719. X}
  720. X
  721. X/*
  722. X** output record.  folds record to terminal width on word boundaries,
  723. X** returning number of lines output.  If limit is reached, remainder
  724. X** of buffer waiting to be output is returned.  Processes several
  725. X** special characters:
  726. X**    form-feed - return "lim" lines so we stop on that line
  727. X**    tabs - counts "expanded" width
  728. X**    backspace - assumes they work, -1 width unless in first col.
  729. X**    bell - pass through with zero width
  730. X**    newline - end of record.
  731. X**    del - turns into '_'
  732. X**    other control - 'A' - 1 added ('01' = ctl-A).  Makes escape = "[".
  733. X**        (prevents "letter bombs" containing inappropriate control
  734. X**            sequences for the terminal).
  735. X**
  736. X** Sets Lookahead pointer to remainder of line or NULL.
  737. X*/
  738. Xstatic do_out(s,lim)
  739. Xchar *s;
  740. Xint lim;
  741. X{
  742. X    int len,i;
  743. X    char cs,*word,*start;
  744. X
  745. X    Lookahead = NULL;
  746. X    if (s == NULL)
  747. X        return(0);
  748. X    len = 0;
  749. X    start = word = s;
  750. X
  751. X    /*
  752. X    ** NOTE: "normal" return is buried inside switch, at newline
  753. X    ** ending record
  754. X    */
  755. X    for (i=0; i < lim; ++i)
  756. X    {
  757. X        for ( ; len < C_allow; ++s)
  758. X        {
  759. X            switch (*s)
  760. X            {
  761. X            case '\n':
  762. X                *s = '\0';    /* fall through */
  763. X            case '\0':
  764. X                printf("%s\n",start);
  765. X                return(i+1);
  766. X            case '\t':
  767. X                len = ((len/PERTAB)+1)*PERTAB;
  768. X                word = s;
  769. X                break;
  770. X            case '\b':
  771. X                if (len > 0)
  772. X                    --len;
  773. X                break;
  774. X            case '\014':
  775. X                *s = ' ';
  776. X                i = lim-1;    /* fall through */
  777. X            case ' ':
  778. X                word = s+1;
  779. X                ++len;
  780. X                break;
  781. X            case '\177':
  782. X                *s = '_';
  783. X                ++len;
  784. X                break;
  785. X            default:
  786. X                if (*s < ' ')
  787. X                    *s += 'A' - 1;
  788. X                ++len;        /* fall through */
  789. X            case '\07':
  790. X                break;
  791. X            }
  792. X        }
  793. X        cs = *s;
  794. X        *s = '\0';
  795. X        if ((len = strlen(word)) < BACKTRACK)
  796. X        {
  797. X            *s = cs;
  798. X            s = word;
  799. X            cs = *s;
  800. X            *s = '\0';
  801. X        }
  802. X        else
  803. X            len = 0;
  804. X        printf("%s\n",start);
  805. X        start = s;
  806. X        *s = cs;
  807. X    }
  808. X    Lookahead = start;
  809. X    return(lim);
  810. X}
  811. X
  812. Xsave_article(tempfname)
  813. Xchar *tempfname;
  814. X{
  815. X    FILE *in, *out;
  816. X    int c;
  817. X    time_t timenow, time();
  818. X    char *today, *ctime();
  819. X
  820. X
  821. X    if ((in = fopen(tempfname, "r")) == NULL)
  822. X        return;
  823. X    if ((out = fopen(Ccfile, "a")) == NULL)
  824. X    {
  825. X        fclose(in);
  826. X        return;
  827. X    }
  828. X    timenow = time((time_t)0);
  829. X    today = ctime(&timenow);
  830. X    fprintf(out,"From vn %s",today);
  831. X    while ((c=getc(in)) != EOF)
  832. X        putc(c, out);
  833. X    putc('\n', out);
  834. X    fclose(in);
  835. X    fclose(out);
  836. X    printf ("a copy has been saved in %s\n", Ccfile);
  837. X}
  838. X
  839. X/*
  840. X    send article to printer
  841. X*/
  842. Xstatic printr ()
  843. X{
  844. X    char cmd[RECLEN];
  845. X
  846. X    tty_set (SAVEMODE);
  847. X    printf("Sent to printer\n");
  848. X    sprintf (cmd,"%s %s 2>/dev/null",Printer,Fname);
  849. X    system (cmd);
  850. X    tty_set (RESTORE);
  851. X}
  852. X
  853. X/*
  854. X    search article for specified search pattern, returning the line on which
  855. X        it is found in buf, a null buffer otherwise. The input file will
  856. X        be positioned either after the line on which the pattern is
  857. X        found, or unaaltered if match fails.
  858. X*/
  859. Xsearcher (buf)
  860. Xchar    *buf;
  861. X{
  862. X    static char    searchstr[RECLEN] = "";
  863. X    char    lasave[RECLEN];
  864. X    char    *s, *reg, *rprompt(), *regcmp(), *regex();
  865. X    long    current;
  866. X    int    orlines;
  867. X
  868. X    /* save position, then request search pattern */
  869. X    current = ftell(Fpread);
  870. X    orlines = Rlines;
  871. X
  872. X     tty_set (SAVEMODE);
  873. X     sprintf (lasave,SEARCHFORM,searchstr);
  874. X    s = rprompt(lasave,lasave);
  875. X    tty_set (RESTORE);
  876. X    if (s != NULL)
  877. X        strcpy(searchstr, lasave);
  878. X    /* Now compile the search string */
  879. X    if(( reg = regcmp(searchstr, (char *)0)) == NULL) {
  880. X        printf("Invalid search string \"%s\"\n", searchstr);
  881. X        *buf = '\0';
  882. X        return;
  883. X    }
  884. X
  885. X    /* try lookahead buffer first */
  886. X    if (Lookahead != NULL && regex(reg,Lookahead) != NULL)
  887. X    {
  888. X        strcpy(buf,Lookahead);
  889. X        regfree(reg);
  890. X        return;
  891. X    }
  892. X
  893. X    /* Lookahead can point into buf */
  894. X    if (Lookahead != NULL)
  895. X        strcpy(lasave,Lookahead);
  896. X
  897. X    /* now start reading lines, rotating if necessary and do search */
  898. X    while (fgets(buf,RECLEN-1,Fpread) != NULL)
  899. X    {
  900. X        if (Rot != 0 && Rlines >= Hlines)
  901. X            rot_line(buf);
  902. X        ++Rlines;
  903. X        if( regex(reg, buf) != NULL ){    /* Got it */
  904. X            term_set (ONREVERSE);
  905. X            printf("\n\tSkipping ....\n\n");
  906. X            term_set (OFFREVERSE);
  907. X            regfree(reg);
  908. X            return;
  909. X        }
  910. X    }
  911. X
  912. X    /* no dice, so restore position */
  913. X    regfree(reg);
  914. X    term_set (ONREVERSE);
  915. X    printf("Cannot find string \"%s\" in remainder of article\007\n",
  916. X        searchstr);
  917. X    term_set (OFFREVERSE);
  918. X    fseek(Fpread,current,0);
  919. X    Rlines = orlines;
  920. X    if (Lookahead != NULL)
  921. X        strcpy(buf,lasave);
  922. X    else
  923. X        *buf = '\0';
  924. X    return(0.0);
  925. X}
  926. @//E*O*F reader.c//
  927. if test 18610 -ne "`wc -c <'reader.c'`"; then
  928.     echo shar: error transmitting "'reader.c'" '(should have been 18610 characters)'
  929. fi
  930. fi # end of overwriting check
  931. echo shar: extracting "'strtok.c'" '(1082 characters)'
  932. if test -f 'strtok.c' ; then 
  933.   echo shar: will not over-write existing file "'strtok.c'"
  934. else
  935. sed 's/^X//' >strtok.c <<'@//E*O*F strtok.c//'
  936. X/*
  937. X** vn news reader.
  938. X**
  939. X** strtok.c - strtok() and strpbrk() string routines using UCB index().
  940. X**
  941. X** see copyright disclaimer / history in vn.c source file
  942. X*/
  943. X
  944. X#include <stdio.h>
  945. X
  946. Xchar *strpbrk (s,del)
  947. Xchar *s, *del;
  948. X{
  949. X    char *ptr,*index();
  950. X    if (s == NULL)
  951. X        return (NULL);
  952. X    for (; *del != '\0'; ++del)
  953. X        if ((ptr = index(s,*del)) != NULL)
  954. X            return (ptr);
  955. X    return (NULL);
  956. X}
  957. X
  958. Xchar *strtok(str,delim)
  959. Xchar *str, *delim;
  960. X{
  961. X    char *tokstart, *tokend, *first_ch (), *last_ch();
  962. X    static char *save=NULL;
  963. X
  964. X    if (str != NULL)
  965. X        save = str;
  966. X
  967. X    if (save == NULL)
  968. X        return (NULL);
  969. X
  970. X    tokstart = first_ch (save, delim);
  971. X    tokend = last_ch (tokstart, delim);
  972. X    save = first_ch (tokend, delim);
  973. X    *tokend = '\0';
  974. X
  975. X    if (*tokstart == '\0')
  976. X        return (NULL);
  977. X
  978. X    return (tokstart);
  979. X}
  980. X
  981. Xstatic char *first_ch (str,delim)
  982. Xchar *str,*delim;
  983. X{
  984. X    char *index ();
  985. X    char *f;
  986. X
  987. X    for (f = str; *f != '\0' && index(delim,*f) != NULL; ++f)
  988. X        ;
  989. X
  990. X    return (f);
  991. X}
  992. X
  993. Xstatic char *last_ch (str,delim)
  994. Xchar *str,*delim;
  995. X{
  996. X    char *index ();
  997. X    char *f;
  998. X
  999. X    for (f = str; *f != '\0' && index(delim,*f) == NULL; ++f)
  1000. X        ;
  1001. X
  1002. X    return (f);
  1003. X}
  1004. @//E*O*F strtok.c//
  1005. if test 1082 -ne "`wc -c <'strtok.c'`"; then
  1006.     echo shar: error transmitting "'strtok.c'" '(should have been 1082 characters)'
  1007. fi
  1008. fi # end of overwriting check
  1009. echo shar: extracting "'term_set.c'" '(4914 characters)'
  1010. if test -f 'term_set.c' ; then 
  1011.   echo shar: will not over-write existing file "'term_set.c'"
  1012. else
  1013. sed 's/^X//' >term_set.c <<'@//E*O*F term_set.c//'
  1014. X/*
  1015. X** vn news reader.
  1016. X**
  1017. X** term_set.c - terminal control, hides termcap interface
  1018. X**
  1019. X** see copyright disclaimer / history in vn.c source file
  1020. X*/
  1021. X
  1022. X#include <stdio.h>
  1023. X#include "tty.h"
  1024. X#include "vn.h"
  1025. X
  1026. Xextern int L_allow, C_allow;
  1027. Xextern char *Ku, *Kd, *Kl, *Kr;    
  1028. X
  1029. Xstatic outc (c)
  1030. Xchar c;
  1031. X{
  1032. X    putchar (c);
  1033. X}
  1034. X
  1035. X/*
  1036. X    term_set controls terminal through termcap
  1037. X    START sets global parameters related to terminal also,
  1038. X    as well as allocating display buffer which depends on
  1039. X    terminal lines, and allocating escape strings.  RESTART
  1040. X    simply re-issues the initialization - used following system
  1041. X    calls that could have goofed up the terminal state.
  1042. X*/
  1043. X
  1044. X/*
  1045. X** Escape strings.
  1046. X*/
  1047. X
  1048. Xstatic char *Cm,*Cl,*So,*Se,*Te,*Bc,*Ce,*Ti,*Ks,*Ke;
  1049. X#ifdef USEVS
  1050. Xstatic char *Vs,*Ve;
  1051. X#endif
  1052. X
  1053. Xstatic int Backspace;        /* backspace works */
  1054. Xstatic int Overstrike;        /* terminal overstrikes */
  1055. X
  1056. Xstatic t_setup()
  1057. X{
  1058. X    int i;
  1059. X    char *tgetstr(), *getenv(), *str_store();
  1060. X    char *c, tc_buf[2048],optstr[2048];
  1061. X
  1062. X    c = optstr;
  1063. X    if (tgetent(tc_buf,getenv("TERM")) != 1)
  1064. X        printex ("%s - unknown terminal",getenv("TERM"));
  1065. X
  1066. X    /* get needed capabilities */
  1067. X    Cm = str_store(tgetstr("cm",&c));
  1068. X    Cl = str_store(tgetstr("cl",&c));
  1069. X    So = str_store(tgetstr("so",&c));
  1070. X    Se = str_store(tgetstr("se",&c));
  1071. X    Te = str_store(tgetstr("te",&c));
  1072. X    Ti = str_store(tgetstr("ti",&c));
  1073. X    Bc = str_store(tgetstr("bc",&c));
  1074. X    Ce = str_store(tgetstr("ce",&c));
  1075. X    Kd = str_store(tgetstr("kd",&c));
  1076. X    Ke = str_store(tgetstr("ke",&c));
  1077. X    Kl = str_store(tgetstr("kl",&c));
  1078. X    Kr = str_store(tgetstr("kr",&c));
  1079. X    Ks = str_store(tgetstr("ks",&c));
  1080. X    Ku = str_store(tgetstr("ku",&c));
  1081. X#ifdef USEVS
  1082. X    Vs = str_store(tgetstr("vs",&c));
  1083. X    Ve = str_store(tgetstr("ve",&c));
  1084. X#endif
  1085. X    Backspace = tgetflag("bs");
  1086. X    Overstrike = tgetflag("os");
  1087. X
  1088. X    if ( *Cm == '\0' || *Cl == '\0')
  1089. X    {
  1090. X        printex ("cursor control and erase capability needed");
  1091. X    }
  1092. X
  1093. X    /*
  1094. X    ** Checks for arrow keys which don't issue something beginning
  1095. X    ** with <ESC>.  This is more paranoid than we need to be, strictly
  1096. X    ** speaking - we could get away with any string which didn't
  1097. X    ** conflict with controls used for commands.  However, that would
  1098. X    ** be a maintenance headache - we will simply reserve <ESC> as the
  1099. X    ** only char not to be used for commands, and punt on terminals
  1100. X    ** which don't send reasonable arrow keys.  It would be confusing
  1101. X    ** to have keys work partially, also.  I know of no terminal with
  1102. X    ** one arrow key beginning with an escape, and another beginning
  1103. X    ** with something else, but let's be safe.  This also insists on
  1104. X    ** definitions for all 4 arrows, which seems reasonable.
  1105. X    */
  1106. X
  1107. X    if ((*Ku != '\0' && *Ku != '\033') || *Kl != *Ku || *Kr != *Ku || *Kd != *Ku)
  1108. X    {
  1109. X        fgprintf("WARNING: arrow keys will not work for this terminal");
  1110. X        Ku = Kd = Kl = Kr = Kd = Ke = "";
  1111. X    }
  1112. X
  1113. X    if (Overstrike)
  1114. X        fgprintf ("WARNING: terminal overstrikes - can't update display without erase\n");
  1115. X
  1116. X    i = RECBIAS+1 < HHLINES+2 ? HHLINES+2 : RECBIAS+1;
  1117. X    if ((L_allow = tgetnum("li")) < i)
  1118. X    {
  1119. X        if (L_allow < 0)
  1120. X            printex ("can't determine number of lines on terminal");
  1121. X        printex ("too few lines for display - %d needed", i);
  1122. X    }
  1123. X
  1124. X    /*
  1125. X    ** C_allow set so as to not use extreme right column.
  1126. X    ** Avoids "bad wraparound" problems - we're deciding it's best
  1127. X    ** to ALWAYS assume no automargin, and take care of it ourselves
  1128. X    */
  1129. X    if((C_allow = tgetnum("co")) > MAX_C)
  1130. X        C_allow = MAX_C;
  1131. X    else
  1132. X        --C_allow;
  1133. X    if (C_allow < MIN_C)
  1134. X    {
  1135. X        if (C_allow < 0)
  1136. X            printex("can't determine number of columns on terminal.");
  1137. X        printex ("too few columns for display - %d needed",MIN_C);
  1138. X    }
  1139. X
  1140. X    L_allow -= RECBIAS;
  1141. X    page_alloc();
  1142. X    tputs(Ti,1,outc);
  1143. X    tputs(Ks,1,outc);
  1144. X#ifdef USEVS
  1145. X    tputs(Vs,1,outc);
  1146. X#endif
  1147. X}
  1148. X
  1149. X/* VARARGS */
  1150. Xterm_set(cmd,x,y)
  1151. Xint cmd,x,y;
  1152. X{
  1153. X    char *tgoto();
  1154. X    int i;
  1155. X    switch (cmd)
  1156. X    {
  1157. X    case MOVE:
  1158. X        tputs (tgoto(Cm,x,y),1,outc);
  1159. X        break;
  1160. X    case ERASE:
  1161. X        tputs(Cl,1,outc);
  1162. X        break;
  1163. X    case ONREVERSE:
  1164. X        tputs(So,1,outc);
  1165. X        break;
  1166. X    case OFFREVERSE:
  1167. X        tputs(Se,1,outc);
  1168. X        break;
  1169. X    case START:
  1170. X        t_setup();
  1171. X        break;
  1172. X    case RESTART:
  1173. X        tputs(Ti,1,outc);
  1174. X        tputs(Ks,1,outc);
  1175. X#ifdef USEVS
  1176. X        tputs(Vs,1,outc);
  1177. X#endif
  1178. X        break;
  1179. X    case STOP:
  1180. X        term_set (MOVE,0,L_allow+RECBIAS-1);
  1181. X        printf ("\n");
  1182. X        tputs(Ke,1,outc);
  1183. X        tputs(Te,1,outc);
  1184. X#ifdef USEVS
  1185. X        tputs(Ve,1,outc);
  1186. X#endif
  1187. X        break;
  1188. X    case RUBSEQ:
  1189. X        if (Overstrike)
  1190. X        {
  1191. X            /* space overprint is futile */
  1192. X            if (Backspace)
  1193. X                putchar('\010');
  1194. X            else
  1195. X                tputs(Bc,1,outc);
  1196. X            break;
  1197. X        }
  1198. X        if (Backspace)
  1199. X            printf("%c %c",'\010','\010');
  1200. X        else
  1201. X        {
  1202. X            tputs(Bc,1,outc);  
  1203. X            putchar(' ');  
  1204. X            tputs(Bc,1,outc);
  1205. X        }
  1206. X        break;
  1207. X    case ZAP:
  1208. X        if (Ce != NULL && *Ce != '\0')
  1209. X            tputs(Ce,1,outc);
  1210. X        else
  1211. X        {
  1212. X            if (Overstrike)
  1213. X                break;        /* punt */
  1214. X            for (i=x; i < y; ++i)
  1215. X                putchar(' ');
  1216. X            if (Backspace)
  1217. X            {
  1218. X                for (i=x; i < y; ++i)
  1219. X                    putchar('\010');
  1220. X            }
  1221. X            else
  1222. X            {
  1223. X                for (i=x; i < y; ++i)
  1224. X                    tputs(Bc,1,outc);
  1225. X            }
  1226. X        }
  1227. X        break;
  1228. X    default:
  1229. X        printex ("term_set unknown code (%d)",cmd);
  1230. X        break;
  1231. X    }
  1232. X    return (0);
  1233. X}
  1234. @//E*O*F term_set.c//
  1235. if test 4914 -ne "`wc -c <'term_set.c'`"; then
  1236.     echo shar: error transmitting "'term_set.c'" '(should have been 4914 characters)'
  1237. fi
  1238. fi # end of overwriting check
  1239. echo shar: extracting "'tty.h'" '(404 characters)'
  1240. if test -f 'tty.h' ; then 
  1241.   echo shar: will not over-write existing file "'tty.h'"
  1242. else
  1243. sed 's/^X//' >tty.h <<'@//E*O*F tty.h//'
  1244. X/*
  1245. X** vn news reader.
  1246. X**
  1247. X** tty.h - codes for tty_set and term_set
  1248. X**
  1249. X** see copyright disclaimer / history in vn.c source file
  1250. X*/
  1251. X
  1252. X#define MOVE 100
  1253. X#define ERASE 101
  1254. X#define START 102
  1255. X#define STOP 103
  1256. X#define RUBSEQ 104
  1257. X#define ZAP 105
  1258. X#define ONREVERSE 106
  1259. X#define OFFREVERSE 107
  1260. X#define RESTART 108
  1261. X
  1262. X#define RAWMODE 200
  1263. X#define COOKED 201
  1264. X#define SAVEMODE 202
  1265. X#define RESTORE 203
  1266. X#define BACKSTOP 204
  1267. @//E*O*F tty.h//
  1268. if test 404 -ne "`wc -c <'tty.h'`"; then
  1269.     echo shar: error transmitting "'tty.h'" '(should have been 404 characters)'
  1270. fi
  1271. fi # end of overwriting check
  1272. echo shar: extracting "'tty_set.c'" '(2552 characters)'
  1273. if test -f 'tty_set.c' ; then 
  1274.   echo shar: will not over-write existing file "'tty_set.c'"
  1275. else
  1276. sed 's/^X//' >tty_set.c <<'@//E*O*F tty_set.c//'
  1277. X/*
  1278. X** vn news reader.
  1279. X**
  1280. X** tty_set.c - interface to ioctl (system tty interface)
  1281. X**
  1282. X** see copyright disclaimer / history in vn.c source file
  1283. X*/
  1284. X
  1285. X#ifdef SYSV
  1286. X#include <termio.h>
  1287. X#else
  1288. X#include <sgtty.h>
  1289. X#endif
  1290. X
  1291. X#include "tty.h"
  1292. X
  1293. Xextern char Erasekey,Killkey;
  1294. X
  1295. X#ifdef SYSV
  1296. Xstatic struct termio C_tp, O_tp;
  1297. X#else
  1298. Xstatic struct sgttyb C_tp;
  1299. Xstatic unsigned short O_lflag;
  1300. X#endif
  1301. X
  1302. Xstatic unsigned S_flag=0;
  1303. Xstatic int R_ignore=0;        /* up/down counter of reset calls to ignore */
  1304. X
  1305. X#define IO_GOT 1    /* have polled for original terminal mode */
  1306. X#define IO_RAW 2    /* in RAW (CBREAK actually) mode */
  1307. X
  1308. X/*
  1309. X    tty_set handles ioctl calls.  SAVEMODE, RESTORE are used around
  1310. X    system calls and interrupts to assure cooked mode, and restore
  1311. X    raw if raw on SAVEMODE.  The pair results in no calls to ioctl
  1312. X    if we are cooked already when SAVEMODE is called, and may be nested,
  1313. X    provided we desire no "restore" of cooked mode after restoring raw.
  1314. X
  1315. X    When we get the original terminal mode, we also save erase and kill.
  1316. X
  1317. X    sig_set makes an ioctl call to get process group leader.  Otherwise
  1318. X    ioctl calls should come through here.
  1319. X*/
  1320. Xtty_set(cmd)
  1321. Xint cmd;
  1322. X{
  1323. X    int rc;
  1324. X    unsigned mask;
  1325. X
  1326. X    switch (cmd)
  1327. X    {
  1328. X    case BACKSTOP:
  1329. X#ifdef JOBCONTROL
  1330. X        if ((rc = ioctl(1,TIOCLGET,&mask)) != 0)
  1331. X            break;
  1332. X        mask |= LTOSTOP;
  1333. X        rc = ioctl(1,TIOCLSET,&mask);
  1334. X#else
  1335. X        rc = 0;
  1336. X#endif
  1337. X        break;
  1338. X    case RAWMODE:
  1339. X        if ((S_flag & IO_RAW) != 0)
  1340. X        {
  1341. X            rc = 0;
  1342. X            break;
  1343. X        }
  1344. X        if ((S_flag & IO_GOT) == 0)
  1345. X        {
  1346. X            /* Save original modes, get erase / kill */
  1347. X#ifdef SYSV
  1348. X            rc = ioctl(0,TCGETA,&C_tp);
  1349. X            O_tp = C_tp;
  1350. X            Erasekey = C_tp.c_cc[VERASE];
  1351. X            Killkey = C_tp.c_cc[VKILL];
  1352. X#else
  1353. X            rc = ioctl(0,TIOCGETP,&C_tp);
  1354. X            O_lflag = C_tp.sg_flags;
  1355. X            Erasekey = C_tp.sg_erase;
  1356. X            Killkey = C_tp.sg_kill;
  1357. X#endif
  1358. X        }
  1359. X#ifdef SYSV
  1360. X        C_tp.c_lflag &= ~(ECHO | ICANON);
  1361. X        C_tp.c_cc[VMIN] = 1;
  1362. X        rc = ioctl(0,TCSETAW,&C_tp);
  1363. X#else
  1364. X        C_tp.sg_flags |= CBREAK;
  1365. X        C_tp.sg_flags &= ~ECHO;
  1366. X        rc = ioctl(0,TIOCSETP,&C_tp);
  1367. X#endif
  1368. X        S_flag = IO_GOT|IO_RAW;
  1369. X        break;
  1370. X    case COOKED:
  1371. X        if ((S_flag & IO_RAW) != 0)
  1372. X        {
  1373. X#ifdef SYSV
  1374. X            C_tp = O_tp;
  1375. X            rc = ioctl(0,TCSETAW,&C_tp);
  1376. X#else
  1377. X            C_tp.sg_flags = O_lflag;
  1378. X            rc = ioctl(0,TIOCSETP,&C_tp);
  1379. X#endif
  1380. X            S_flag &= ~IO_RAW;
  1381. X        }
  1382. X        else
  1383. X            rc = 0;
  1384. X        break;
  1385. X    case SAVEMODE:
  1386. X        if ((S_flag & IO_RAW) != 0)
  1387. X        {
  1388. X            tty_set(COOKED);
  1389. X            R_ignore = 0;
  1390. X        }
  1391. X        else
  1392. X            ++R_ignore;
  1393. X        rc = 0;
  1394. X        break;
  1395. X    case RESTORE:
  1396. X        if (R_ignore <= 0)
  1397. X        {
  1398. X            tty_set(RAWMODE);
  1399. X        }
  1400. X        else
  1401. X            --R_ignore;
  1402. X        rc = 0;
  1403. X        break;
  1404. X    default:
  1405. X        rc = -1;
  1406. X    }
  1407. X    if (rc < 0)
  1408. X        printex ("ioctl failure, tty_set: %d",cmd);
  1409. X}
  1410. @//E*O*F tty_set.c//
  1411. if test 2552 -ne "`wc -c <'tty_set.c'`"; then
  1412.     echo shar: error transmitting "'tty_set.c'" '(should have been 2552 characters)'
  1413. fi
  1414. fi # end of overwriting check
  1415. echo shar: extracting "'tune.h'" '(2613 characters)'
  1416. if test -f 'tune.h' ; then 
  1417.   echo shar: will not over-write existing file "'tune.h'"
  1418. else
  1419. sed 's/^X//' >tune.h <<'@//E*O*F tune.h//'
  1420. X/*
  1421. X** vn news reader.
  1422. X**
  1423. X** tune.h - system tuning parameters
  1424. X**
  1425. X** see copyright disclaimer / history in vn.c source file
  1426. X*/
  1427. X
  1428. X/*
  1429. X**    buffer size needed for tmpnam()
  1430. X*/
  1431. X#ifndef L_tmpnam
  1432. X#define L_tmpnam 48
  1433. X#endif
  1434. X
  1435. X/*
  1436. X** hash table size.  linked list type of table which can expand to
  1437. X** arbitrary density, including densities > 100%.  Number of entries
  1438. X** will be number of newsgroups in active list.  This should be a prime
  1439. X** number ("long division" of string modulo table size hash function).
  1440. X*/
  1441. X#define HASHSIZE 809
  1442. X
  1443. X/*
  1444. X**    maximum number of columns on terminal.  If made smaller, there
  1445. X**    will be a savings in the size of the temporary file used
  1446. X**    for holding displays, at the penalty of not being able to use
  1447. X**    the entire screen width on terminals actually possessing more
  1448. X**    columns than this.  A block roughly on the order of this value
  1449. X**    times the number of lines the terminal has is maintained per page in
  1450. X**    the temp file, and read / written as displays are interacted
  1451. X**    with.  MIN_C put here because MAX_C > MIN_C.  MIN_C is the minumum
  1452. X**    number of columns for which a "reasonable" display can be produced.
  1453. X**    before making it smaller, look at all uses of C_allow and variable
  1454. X**    to see that a setting that small won't screw up array bounds.
  1455. X*/
  1456. X#define MAX_C 132
  1457. X#define MIN_C 36
  1458. X
  1459. X/*
  1460. X**    large size for general purpose local buffers.  only used in automatic
  1461. X**    variable declarations.  Used with fgets for buffer size when reading
  1462. X**    file records, to hold pathnames, commands, etc.  Reduce if you blow
  1463. X**    out stack storage.  If reduced too far, will eventually show up
  1464. X**    as syntax errors on reading .newsrc's and the active list, and
  1465. X**    scrozzled article information arising from truncated header lines.
  1466. X**    The reply path line will probably be the first thing to cause trouble.
  1467. X**    Look through the reader to find the worst case chain of declarations
  1468. X**    (on the order of 12 or so is probably the max).
  1469. X*/
  1470. X#define RECLEN 1200
  1471. X
  1472. X/*
  1473. X**    to protect against reading entire articles to find non-existent header
  1474. X**    lines if an article should be hosed, only a limited number of records
  1475. X**    are searched.  Should be big enough to get down to the last header
  1476. X**    entry on legitimate articles.
  1477. X*/
  1478. X#define HDR_LINES 24    /* records of article to search for header line */
  1479. X
  1480. X/* these determine some static array sizes */
  1481. X#define OPTLINES 60    /* maximum number of option lines in .newsrc */
  1482. X#define NUMFILTER 24    /* max number of filters on articles */
  1483. X
  1484. X/* block sizes for allocation routines */
  1485. X#define STRBLKSIZE 1800    /* string storage allocation block */
  1486. X#define NDBLKSIZE 50    /* NODE structures to allocate at a time */
  1487. @//E*O*F tune.h//
  1488. if test 2613 -ne "`wc -c <'tune.h'`"; then
  1489.     echo shar: error transmitting "'tune.h'" '(should have been 2613 characters)'
  1490. fi
  1491. fi # end of overwriting check
  1492. echo shar: extracting "'userlist.c'" '(1923 characters)'
  1493. if test -f 'userlist.c' ; then 
  1494.   echo shar: will not over-write existing file "'userlist.c'"
  1495. else
  1496. sed 's/^X//' >userlist.c <<'@//E*O*F userlist.c//'
  1497. X/*
  1498. X** vn news reader.
  1499. X**
  1500. X** userlist.c - generate user's list of articles
  1501. X**
  1502. X** see copyright disclaimer / history in vn.c source file
  1503. X*/
  1504. X
  1505. X#include <stdio.h>
  1506. X#include "vn.h"
  1507. X
  1508. Xextern PAGE Page;
  1509. X
  1510. X/*
  1511. X    generate user list of articles - either article numbers
  1512. X    are input directly (numeric list), or input is a search
  1513. X    string - invoke regular expression library and examine titles
  1514. X    search string "*" reserved for marked articles.  Strings may
  1515. X    be prefixed with '!' for negation.
  1516. X*/
  1517. Xuserlist (list)
  1518. Xchar *list;
  1519. X{
  1520. X    int i,j,anum[RECLEN/2],acount;
  1521. X    char neg, *s, sbuf[MAX_C+1], *reg, *regex(), *regcmp(), *index(), *strtok();
  1522. X
  1523. X    user_str (sbuf,"Articles or title search string : ",1);
  1524. X    if (sbuf[0] == '!')
  1525. X    {
  1526. X        neg = '!';
  1527. X        s = sbuf+1;
  1528. X    }
  1529. X    else
  1530. X    {
  1531. X        neg = '\0';
  1532. X        s = sbuf;
  1533. X    }
  1534. X    for (i=0; s[i] != '\0'; ++i)
  1535. X    {
  1536. X        if (index(LIST_SEP,s[i]) == NULL)
  1537. X        {
  1538. X            if (s[i] < '0' || s[i] > '9')
  1539. X                break;
  1540. X        }
  1541. X    }
  1542. X    acount = 0;
  1543. X
  1544. X    if (s[i] == '\0')
  1545. X    {
  1546. X        for (s = strtok(s,LIST_SEP); s != NULL; s = strtok(NULL,LIST_SEP))
  1547. X        {
  1548. X            anum[acount] = atoi(s);
  1549. X            ++acount;
  1550. X        }
  1551. X    }
  1552. X    else
  1553. X    {
  1554. X        if (s[0] == ART_MARK)
  1555. X        {
  1556. X            for (i=0; i < Page.h.artnum; ++i)
  1557. X            {
  1558. X                if (Page.b[i].art_mark == ART_MARK)
  1559. X                {
  1560. X                    anum[acount] = Page.b[i].art_id;
  1561. X                    ++acount;
  1562. X                }
  1563. X            }
  1564. X        }
  1565. X        else
  1566. X        {
  1567. X            reg = regcmp(s,(char *) 0);
  1568. X            if (reg != NULL)
  1569. X            {
  1570. X                for (i=0; i < Page.h.artnum; ++i)
  1571. X                {
  1572. X                    if (regex(reg,Page.b[i].art_t) != NULL)
  1573. X                    {
  1574. X                        anum[acount] = Page.b[i].art_id;
  1575. X                        ++acount;
  1576. X                    }
  1577. X                }
  1578. X                regfree (reg);
  1579. X            }
  1580. X            else
  1581. X                preinfo ("bad regular expression syntax");
  1582. X        }
  1583. X    }
  1584. X
  1585. X    /* algorithm is inefficient, but we're only handling a few numbers */
  1586. X    *list = '\0';
  1587. X    for (i=0; i < Page.h.artnum; ++i)
  1588. X    {
  1589. X        for (j=0; j < acount && anum[j] != Page.b[i].art_id; ++j)
  1590. X            ;
  1591. X        if (neg == '!')
  1592. X        {
  1593. X            if (j < acount)
  1594. X                continue;
  1595. X        }
  1596. X        else
  1597. X        {
  1598. X            if (j >= acount)
  1599. X                continue;
  1600. X        }
  1601. X        sprintf (list,"%d ",Page.b[i].art_id);
  1602. X        list += strlen(list);
  1603. X    }
  1604. X}
  1605. @//E*O*F userlist.c//
  1606. if test 1923 -ne "`wc -c <'userlist.c'`"; then
  1607.     echo shar: error transmitting "'userlist.c'" '(should have been 1923 characters)'
  1608. fi
  1609. fi # end of overwriting check
  1610. echo shar: extracting "'vn.h'" '(4278 characters)'
  1611. if test -f 'vn.h' ; then 
  1612.   echo shar: will not over-write existing file "'vn.h'"
  1613. else
  1614. sed 's/^X//' >vn.h <<'@//E*O*F vn.h//'
  1615. X/*
  1616. X** vn news reader.
  1617. X**
  1618. X** vn.h - general parameters
  1619. X**
  1620. X** see copyright disclaimer / history in vn.c source file
  1621. X*/
  1622. X
  1623. X#include "tune.h"
  1624. X
  1625. X#define TRUE 1
  1626. X#define FALSE 0
  1627. X
  1628. X#ifdef OLDRC
  1629. X#define NARGOPT "lprxfuMs"
  1630. X#else
  1631. X#define NARGOPT "lprxfuMsi"
  1632. X#endif
  1633. X
  1634. X#define FIL_AUTHOR 'w'
  1635. X#define FIL_TITLE 't'
  1636. X
  1637. X/*
  1638. X    newsrc states
  1639. X*/
  1640. X#define NEWS_ON ':'
  1641. X#define NEWS_OFF '!'
  1642. X
  1643. X/* bit flags for state of newsgroup */
  1644. X#define FLG_SCAN 1
  1645. X#define FLG_SUB 2
  1646. X#define FLG_PAGE 4
  1647. X#define FLG_WRIT 8
  1648. X#define FLG_SPEC 16
  1649. X
  1650. X#define LIST_SEP "     ,"
  1651. X#define ED_MARK '>'
  1652. X#define ART_MARK '*'
  1653. X#define ART_WRITTEN '_'
  1654. X#define ART_UNWRITTEN ' '
  1655. X
  1656. X#define FPFIX "Re: "
  1657. X#define FPFLEN 4
  1658. X
  1659. X#define ANFORM ":%s - %c for help:\n"
  1660. X#define ANFLINES 1
  1661. X#define NOFORM "can't open article %s\n"
  1662. X#define NEWGFORM "groups not mentioned in %s:\n"
  1663. X#define SAVFORM "save file (%s) ? "
  1664. X#define UDKFORM "undefined key - %c for help"
  1665. X#define HELPFORM "%c for help"
  1666. X
  1667. X/*
  1668. X    page display format and dependent parameters
  1669. X*/
  1670. X#define HFORMAT "\n%s (page %d of %d):"
  1671. X#define DHFORMAT "\n%s (DIGEST EXTRACTION):"
  1672. X#define TFORMAT "%s ~ %s %s"
  1673. X#define AFORMAT "\n%c%c%d) "    /* begin with newline - see show routine */
  1674. X#define CFORMAT "page %d of %d (%d shown), newsgroup %d of %d"
  1675. X#define RECBIAS 2    /* lines before articles - depends on HFORMAT */
  1676. X#define AFLEN 5        /* min. char. in article id - depends on AFORMAT */
  1677. X#define WRCOL 1        /* column of written mark.  depends on AFORMAT */
  1678. X#define INFOLINE 0    /* HFORMAT TFORMAT leaves for use */
  1679. X
  1680. X/*
  1681. X    command characters - don't use numerics or <ESC>
  1682. X    ALTSAVE is a hack to avoid having to use ctl-s - XON/XOFF.
  1683. X    Wanted to preserve "s" pneumonic and lower / control /cap
  1684. X    convention.
  1685. X*/
  1686. X#define DIGEST 'd'
  1687. X#define UP 'k'
  1688. X#define DOWN 'j'
  1689. X#define FORWARD '\012'
  1690. X#define BACK '\010'
  1691. X#define READ 'r'
  1692. X#define ALTREAD ' '
  1693. X#define READALL 'R'
  1694. X#define READSTRING '\022'
  1695. X#define SAVE 's'
  1696. X#define SAVEALL 'S'
  1697. X#define SAVESTRING '\023'
  1698. X#define ALTSAVE '\024'
  1699. X#define PRINT 'p'
  1700. X#define PRINTALL 'P'
  1701. X#define PRINTSTRING '\020'
  1702. X#define MARK 'x'
  1703. X#define UNMARK 'X'
  1704. X#define REDRAW '\014'
  1705. X#define QUIT 'q'
  1706. X#define SSTAT '#'
  1707. X#define GRPLIST '%'
  1708. X#define ORGGRP 'o'
  1709. X#define ORGSTAT 'O'
  1710. X#define UPDATE 'w'
  1711. X#define UNSUBSCRIBE 'u'
  1712. X#define UPALL 'W'
  1713. X#define UPSEEN '\027'
  1714. X#define UNESC '!'
  1715. X#define NEWGROUP 'n'
  1716. X#define HEADTOG 'h'
  1717. X#define SETROT 'z'
  1718. X#define HELP '?'
  1719. X#define HELP_HEAD "[...] = effect of optional number preceding command\n\
  1720. Xpipes are specified by filenames beginning with |\n\
  1721. Xarticles specified as a list of numbers, title search string, or\n\
  1722. X    * to specify marked articles.  ! may be used to negate any\n"
  1723. X
  1724. X#define HHLINES 5    /* lines (CRs + 1) contained in HELP_HEAD */
  1725. X
  1726. X/*
  1727. X    state flags for handling breaks / values for sig_set calls.
  1728. X    BRK_IN, BRK_SESS, BRK_READ and BRK_OUT are the states.  All
  1729. X    but BRK_INIT are used as calls to sig_set.  BRK_RFIN indicates
  1730. X    a return from BRK_READ to BRK_SESS (no jump location passed),
  1731. X*/
  1732. X#define BRK_INIT 0        /* initial value, indicating uncaught signals */
  1733. X#define BRK_IN 1        /* in NEWSRC / article scanning phase */
  1734. X#define BRK_SESS 2        /* in page interactive session */
  1735. X#define BRK_READ 3        /* reading articles */
  1736. X#define BRK_RFIN 4        /* finished reading, return to old mode */
  1737. X#define BRK_OUT 5        /* NEWSRC updating phase */
  1738. X
  1739. X#define BRK_PR "really quit ? "
  1740. X#define BRK_MSG "\nQUIT (signal %d)"
  1741. X
  1742. X/*
  1743. X    newsgroup structure (node of hash table)
  1744. X    next - hashtable link
  1745. X    nd_name - name of newsgroup (key to reach node by)
  1746. X    pnum - page number, initially used to establish Newsorder
  1747. X    pages - number of pages for news display
  1748. X    rdnum - articles read
  1749. X    orgrd - original articles read number
  1750. X    pgshwn - pages shown mask
  1751. X    pgrd - article number on highest conecutively shown page
  1752. X    art - articles in group
  1753. X    state - status
  1754. X*/
  1755. Xtypedef struct _node
  1756. X{
  1757. X    struct _node *next;
  1758. X    char *nd_name;
  1759. X    int pnum,pages,art,rdnum,orgrd,pgrd;
  1760. X    unsigned long pgshwn;
  1761. X    unsigned state;
  1762. X} NODE;
  1763. X
  1764. X/*
  1765. X    newsgroup information for page display
  1766. X    name - of group
  1767. X    group - pointer to table entry
  1768. X    artnum - number of articles
  1769. X*/
  1770. Xtypedef struct
  1771. X{
  1772. X    char *name;
  1773. X    NODE *group;
  1774. X    int artnum;
  1775. X} HEAD;
  1776. X
  1777. X/*
  1778. X    article information - id (spool) number, title string, mark, written.
  1779. X*/
  1780. Xtypedef struct
  1781. X{
  1782. X    int art_id;
  1783. X    char art_mark;
  1784. X    char art_written;
  1785. X    char art_t[MAX_C-AFLEN];
  1786. X} BODY;
  1787. X
  1788. Xtypedef struct
  1789. X{
  1790. X    HEAD h;
  1791. X    BODY *b;
  1792. X} PAGE;
  1793. @//E*O*F vn.h//
  1794. if test 4278 -ne "`wc -c <'vn.h'`"; then
  1795.     echo shar: error transmitting "'vn.h'" '(should have been 4278 characters)'
  1796. fi
  1797. fi # end of overwriting check
  1798. echo shar: extracting "'vnglob.c'" '(1613 characters)'
  1799. if test -f 'vnglob.c' ; then 
  1800.   echo shar: will not over-write existing file "'vnglob.c'"
  1801. else
  1802. sed 's/^X//' >vnglob.c <<'@//E*O*F vnglob.c//'
  1803. X/*
  1804. X** vn news reader.
  1805. X**
  1806. X** vnglob.c - global variables - see string.c also
  1807. X**
  1808. X** see copyright disclaimer / history in vn.c source file
  1809. X*/
  1810. X
  1811. X#include <stdio.h>
  1812. X#include "config.h"
  1813. X#include "vn.h"
  1814. X#include "head.h"
  1815. X
  1816. X/*
  1817. X    global data structure
  1818. X*/
  1819. XNODE **Newsorder;        /* .newsrc file order */
  1820. X
  1821. Xchar *Editor,*Ps1,*Mailer,*Printer,*Poster;
  1822. X
  1823. Xchar Erasekey, Killkey;        /* user keys from stty */
  1824. Xchar *Newsrc, *Orgdir;        /* .newsrc file, and original pwd */
  1825. Xchar *Onews;            /* temp. file for backing up .newsrc */
  1826. Xchar *Savefile = DEF_SAVE;    /* file in which to save articles */
  1827. Xchar *Savedir;            /* default directory for saved articles */
  1828. Xchar *Ccfile;            /* author_copy file, stored /bin/mail fmt */
  1829. X
  1830. Xint Rot;    /* rotation */
  1831. Xint Headflag;    /* header printing flag */
  1832. Xint Digest;    /* if non-zero, digest article */
  1833. X
  1834. Xchar *Ku, *Kd, *Kl, *Kr;    /* Cursor movement capabilities */
  1835. X
  1836. X/* character translation arrays for commands */
  1837. Xchar Cxitop[128], Cxitor[128], Cxrtoi[128], Cxptoi[128];
  1838. X
  1839. X/*
  1840. X    cur_page - current page displayed;
  1841. X    lrec - last record
  1842. X    l_allow - lines allowed for article display
  1843. X    c_allow - columns allowed
  1844. X    ncount = newsorder index
  1845. X    nfltr - number of filters
  1846. X*/
  1847. Xint Cur_page, Lrec, L_allow, C_allow, Ncount, Nfltr;
  1848. X
  1849. X/*
  1850. X    article filtration options.
  1851. X*/
  1852. Xchar *Wopt[NUMFILTER];        /* regular expressions for -w options */
  1853. Xchar *Topt[NUMFILTER];        /* regular expressions for -t options */
  1854. Xchar *Negwopt[NUMFILTER];    /* regular expressions for negated -w options */
  1855. Xchar *Negtopt[NUMFILTER];    /* regular expressions for negated -t options */
  1856. X
  1857. Xint Nwopt, Ntopt, Nnwopt, Nntopt;
  1858. X
  1859. Xint Nounsub, Listfirst;
  1860. X/*
  1861. X    current page
  1862. X*/
  1863. XPAGE Page;
  1864. @//E*O*F vnglob.c//
  1865. if test 1613 -ne "`wc -c <'vnglob.c'`"; then
  1866.     echo shar: error transmitting "'vnglob.c'" '(should have been 1613 characters)'
  1867. fi
  1868. fi # end of overwriting check
  1869. echo shar: "End of archive 2 (of 3)."
  1870. cp /dev/null ark2isdone
  1871. DONE=true
  1872. for I in 1 2 3; do
  1873.     if test -! f ark${I}isdone; then
  1874.         echo "You still need to run archive ${I}."
  1875.         DONE=false
  1876.     fi
  1877. done
  1878. case $DONE in
  1879.     true)
  1880.         echo "You have run all 3 archives."
  1881.         echo 'Now read the README'
  1882.         ;;
  1883. esac
  1884. ##  End of shell archive.
  1885. exit 0
  1886.